<script setup lang="ts">
import { h, ref, computed } from "vue"
import { Spin, Divider, Button } from "ant-design-vue"
import { DefaultOptionType, SelectValue } from "ant-design-vue/es/select"
import Input from "../input/Input.vue"
import Select from "../select/Select.vue"
import { PaginationParams, SearchDataSource } from "@/types"
import { useConfigContextInject } from "ant-design-vue/es/config-provider/context"

const configContext = useConfigContextInject()

/**
 * 处理服务器响应
 */
export type Formatter = (data: any) => DefaultOptionType

export interface Props {
  datasource: SearchDataSource
  formatter?: Formatter
  valueIndex?: string[]
  labelIndex?: string[]
  placeholder?: string
}

// 用于双向绑定下拉选项选中的值
const model = defineModel<SelectValue>()

// 定义传入的参数
const props = withDefaults(defineProps<Props>(), {
  labelIndex: () => ["label"],
  valueIndex: () => ["value"],
  placeholder: "输入关键字以搜索",
})

// 搜索使用的关键词
const keyword = ref("")

// 分页参数
const pagination = ref<PaginationParams>({ page: 1, size: 10 })

// 定义请求选项的请求
const { isFetching, data, error, execute } = props.datasource(
  computed(() => ({ keyword: keyword.value })),
  pagination,
)

const options = ref<any[]>([])

// 搜索框
const inputRef = ref()
const computedPlaceholder = computed(() => {
  if (props.placeholder && props.placeholder !== "输入关键字以搜索")
    return props.placeholder
  return configContext?.locale?.value.locale === "zh-cn"
    ? "输入关键字以搜索"
    : "Input keywords to search"
})

function onSelectVisibleChange(open: boolean) {
  if (open) {
    // 如果查询参数的接口已经执行了，则组装选项
    if (data.value && data.value?.content?.length > 0) {
      options.value = parseOptions()
    }

    // 如果打开下拉选项时没有可用选项，则刷新
    if (!options.value || options.value.length === 0) {
      onSearch("")
    }

    setTimeout(() => {
      inputRef.value?.focus()
    }, 0)
  }
}

// 用于保存前序执行的查询任务的索引
let timeoutTaskIndex: ReturnType<typeof setTimeout> | null = null

// 当关键词变化时，修改关键词并重置页码
function onSearch(value: string) {
  // 如果有未完成的请求，取消它
  if (timeoutTaskIndex) {
    clearTimeout(timeoutTaskIndex)
  }

  options.value = []
  keyword.value = value
  pagination.value.page = 1

  // 延时一段时间后执行查询
  timeoutTaskIndex = setTimeout(
    () => {
      timeoutTaskIndex = null

      // 使用返回的值更新选项
      execute().then(() => {
        options.value = parseOptions()
      })
    },
    value ? 500 : 0,
  )
}

// 从响应值中获取的参数选项
function parseOptions() {
  const content = data.value?.content as any[]
  if (!content || content.length === 0) {
    return []
  }

  if (props.formatter) {
    return data.value!.content.map((item: any) => props.formatter!(item))
  }

  return content?.map((item) => {
    const value = getValueByIndex(item, props.valueIndex)
    const label = getValueByIndex(item, props.labelIndex)
    return {
      label,
      value,
    }
  })
}

function getValueByIndex(obj: any, key: string[]): any {
  return key.reduce((acc, cur) => acc[cur], obj)
}

// 存在选项，且当前页不是最后一页时，显示加载更多按钮
const showLoadMoreButton = computed(() => {
  return (
    options.value &&
    options.value.length > 0 &&
    data.value &&
    data.value.total > options.value.length
  )
})

// 加载更多选项
function onLoadMore() {
  pagination.value.page += 1
  execute().then(() => {
    options.value = options.value.concat(parseOptions())
  })

  setTimeout(() => {
    inputRef.value?.focus()
  }, 0)
}
</script>

<template>
  <Select
    class="pc-w-full"
    v-model="model"
    v-bind="$attrs"
    :show-search="false"
    :options="options"
    :filter-option="false"
    @dropdownVisibleChange="onSelectVisibleChange"
  >
    <template v-if="error">
      {{ error }}
    </template>
    <template #dropdownRender="{ menuNode }">
      <Input
        ref="inputRef"
        class="pc-mb-2"
        v-model="keyword"
        :bordered="true"
        :placeholder="computedPlaceholder"
        @change="onSearch(keyword)"
        @pressEnter="onSearch(keyword)"
      >
        <template #prefix>
          <h-icon-search v-if="!isFetching" />
          <Spin v-else size="small" />
        </template>
      </Input>
      <component :is="h(menuNode)" />
      <Divider v-show="showLoadMoreButton" class="pc-my-1" />
      <Button
        v-if="showLoadMoreButton"
        class="pc-w-full"
        type="text"
        :disabled="isFetching"
        @click.prevent="onLoadMore"
      >
        加载更多
      </Button>
    </template>
  </Select>
</template>
